import os
import sys
import uuid
from PySide6.QtCore import QObject, Signal, Slot, QUrl, QUrlQuery,QTimer,QByteArray
from PySide6.QtWidgets import QApplication
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
import json
import time

from loguru import logger
# 手动补充 patch 请求emu
QNetworkAccessManager.Operation.PatchOperation = 0x6 # PATCH

class API_Interface_Schemas(object):
    """配置文件结构"""
    LOGIN = ["login","/api/v1/access-token",QNetworkAccessManager.Operation.PostOperation]
    REFRESH = ["refresh","/api/v1/refresh-token",QNetworkAccessManager.Operation.PostOperation]
    ROUTES = ["routes","/api/v1/async-routes",QNetworkAccessManager.Operation.GetOperation]
    # auth api
    USER_ADD = ["user_add","/api/v1/users/add",QNetworkAccessManager.Operation.PostOperation]
    USER_DEL = ["user_del","/api/v1/users/del/{user_id}",QNetworkAccessManager.Operation.DeleteOperation]
    USER_BULKDEL = ["user_user_add","/api/v1/users/bulkdel",QNetworkAccessManager.Operation.DeleteOperation]
    USER_SET = ["user_set","/api/v1/users/set/{user_id}",QNetworkAccessManager.Operation.PatchOperation]
    USER_QUERY = ["user_query","/api/v1/users/query",QNetworkAccessManager.Operation.GetOperation]
    USER_GET = ["user_get","/api/v1/users/get",QNetworkAccessManager.Operation.GetOperation]
    USER_PWDSET = ["user_pwdset","/api/v1/users/pwdset",QNetworkAccessManager.Operation.PostOperation]
    USER_OTPRESET = ["user_otpreset","/api/v1/users/otpreset/{user_id}",QNetworkAccessManager.Operation.PostOperation]
    ROLES_ADD = ["role_add","/api/v1/roles/add",QNetworkAccessManager.Operation.PostOperation]
    ROLES_DEL = ["role_del","/api/v1/roles/del/{role_id}",QNetworkAccessManager.Operation.DeleteOperation]
    ROLES_SET = ["role_set","/api/v1/roles/set/{role_id}",QNetworkAccessManager.Operation.PatchOperation]
    ROLES_LIST = ["role_list","/api/v1/roles/list",QNetworkAccessManager.Operation.GetOperation]
    MENUS_ADD = ["menu_add","/api/v1/menus/add",QNetworkAccessManager.Operation.PostOperation]
    MENUS_DEL = ["menu_del","/api/v1/menus/del/{menu_id}",QNetworkAccessManager.Operation.DeleteOperation]
    MENUS_SET = ["menu_set","/api/v1/menus/set/{menu_id}",QNetworkAccessManager.Operation.PatchOperation]
    MENUS_LIST = ["menu_list","/api/v1/menus/list",QNetworkAccessManager.Operation.GetOperation]
    # system api
    SETTINGS_GET = ["settings_get","/api/v1/system/settings/get",QNetworkAccessManager.Operation.GetOperation]
    SETTINGS_SET = ["settings_set","/api/v1/system/settings/set",QNetworkAccessManager.Operation.PatchOperation]
    SETTINGS_SYNCLDAP = ["settings_syncldap","/api/v1/system/settings/syncldap",QNetworkAccessManager.Operation.PostOperation]
    # datasets
    DATASET_ADD = ["dataset_add","/api/v1/data_task/datasets/add",QNetworkAccessManager.Operation.PostOperation]
    DATASET_ADDFILES = ["dataset_addfiles","/api/v1/data_task/datasets/addfiles",QNetworkAccessManager.Operation.PostOperation]
    DATASET_DEL = ["dataset_del","/api/v1/data_task/datasets/del/{dataset_id}",QNetworkAccessManager.Operation.DeleteOperation]
    DATASET_SET = ["dataset_set","/api/v1/data_task/datasets/set/{dataset_id}",QNetworkAccessManager.Operation.PatchOperation]
    DATASETS_QUERY = ["datasets_query","/api/v1/data_task/datasets/query",QNetworkAccessManager.Operation.GetOperation]
    # projects
    PROJECT_ADD     =       ["project_add","/api/v1/data_task/project/add",QNetworkAccessManager.Operation.PostOperation]
    PROJECT_DEL     =       ["project_del","/api/v1/data_task/project/del/{project_id}",QNetworkAccessManager.Operation.DeleteOperation]
    PROJECT_SET     =       ["project_set","/api/v1/data_task/project/set/{project_id}",QNetworkAccessManager.Operation.PatchOperation]
    PROJECT_QUERY   =       ["project_query","/api/v1/data_task/project/query",QNetworkAccessManager.Operation.GetOperation]
    # tasks
    TASK_ADD        =       ["task_add",    "/api/v1/data_task/annotation_task/add",            QNetworkAccessManager.Operation.PostOperation]
    TASK_DEL        =       ["task_del",    "/api/v1/data_task/annotation_task/del/{task_id}",  QNetworkAccessManager.Operation.DeleteOperation]
    TASK_SET        =       ["task_set",    "/api/v1/data_task/annotation_task/set/{task_id}",  QNetworkAccessManager.Operation.PatchOperation]
    TASK_QUERY      =       ["task_query",  "/api/v1/data_task/annotation_task/query",          QNetworkAccessManager.Operation.GetOperation]
    # AI models
    MODEL_ADD = ["model_add","/api/v1/intelligence_model/model/add",QNetworkAccessManager.Operation.PostOperation]
    MODEL_DEL = ["model_del","/api/v1/intelligence_model/model/del/{model_id}",QNetworkAccessManager.Operation.DeleteOperation]
    MODEL_SET = ["model_set","/api/v1/intelligence_model/model/set/{model_id}",QNetworkAccessManager.Operation.PatchOperation]
    MODELS_QUERY = ["model_query","/api/v1/intelligence_model/model/query",QNetworkAccessManager.Operation.GetOperation]
    
            
            # "get_user_info": ["/api/v1/access_token",QNetworkAccessManager.Operation.DeleteOperation],


class API(QObject):
    """API 类，处理所有网络请求和认证管理"""
    
    request_result = Signal(str, dict, bool)  # (命令类型, 结果数据, 是否成功)
    request_error = Signal(str)   # 请求错误信号(错误信息)
    token_refreshed = Signal()    # Token刷新成功信号
    
    def __init__(self, base_url):
        super().__init__()
        self.base_url = base_url.rstrip("/")  # 确保base_url不以/结尾
        self.nam = QNetworkAccessManager()
        self.access_token = None
        self.refresh_token = None
        self.expires_at = 0  # token过期时间戳(绝对时间)
        self.requests = {}   # 存储请求信息: {request_id: {type, callback, timestamp}}
        # 连接信号
        self.nam.finished.connect(self._handle_response)
        
        # 登录状态检查定时器
        self.timer_check_login_status = QTimer()
        self.timer_check_login_status.setInterval(30000)  # 30秒检查一次
        self.timer_check_login_status.timeout.connect(self.check_login_status)
        self.timer_check_login_status.start()
    
    def set_userinfo(self, access_token=None, refresh_token=None, expires_in=None, **kwargs):
        """设置用户认证信息"""
        if access_token:
            self.access_token = access_token
        if refresh_token:
            self.refresh_token = refresh_token
        if expires_in:
            # 计算过期时间戳(当前时间+有效期秒数)
            self.expires_at = expires_in
    
    def login(self, username, password):
        """用户登录并获取token"""
        data = {"grant_type": "password", "username": username, "password": password}
        self._send_request(API_Interface_Schemas.LOGIN, data)
    
    def logout(self):
        """用户登出"""
        self._send_request(API_Interface_Schemas.LOGOUT)
        self.access_token = None
        self.refresh_token = None
        self.expires_at = 0
        
    #  region 数据集管理
    def query_datasets(self,name="",desc="",data_type="",create_at=[],update_at=[],limit=10,page=1,order_by="create_at",callback=None):
        data = {
            "name":name,
            "desc":desc,
            "data_type":data_type,
            "create_at":create_at,
            "update_at":update_at,
            "limit":limit,
            "page":page,
            "order_by":order_by
        }
        self._send_request(API_Interface_Schemas.DATASETS_QUERY,query_data=data,callback=callback)
        
    def add_dataset(self,name="",desc="",data_type=1,callback=None):
        data = {
            "name":name,
            "desc":desc,
            "data_type":data_type,
        }
        self._send_request(API_Interface_Schemas.DATASET_ADD,data=data,callback=callback)
        
    def attach_file_datasets(self,dataset_id="",file_path ="",callback=None):
        data = {
            "id":dataset_id,
            "file_path":file_path,
        }
        self._send_request(API_Interface_Schemas.DATASET_ADDFILES,data=data,callback=callback)
        
        
    def del_dataset(self,dataset_id=None,callback=None):
        data = {
            "dataset_id":dataset_id,
        }
        self._send_request(API_Interface_Schemas.DATASET_DEL,url_kwargs=data,callback=callback)
        
    # endregion
    
    #  region 项目管理
    def query_projects(self,name="",desc="",attributes="",create_at=[],update_at=[],limit=10,page=1,order_by="create_at",callback=None):
        data = {
            "name":name,
            "desc":desc,
            "attributes":attributes,
            "create_at":create_at,
            "update_at":update_at,
            "limit":limit,
            "page":page,
            "order_by":order_by
        }
        self._send_request(API_Interface_Schemas.PROJECT_QUERY,query_data=data,callback=callback)
        
    def add_project(self,name="",desc="",attributes={},callback=None):
        data = {
            "name":name,
            "desc":desc,
            "attributes":attributes,
        }
        self._send_request(API_Interface_Schemas.PROJECT_ADD,data=data,callback=callback)
        
    def set_project(self,name="",desc="",attributes={},callback=None):
        data = {
            "name":name,
            "desc":desc,
            "attributes":attributes,
        }
        self._send_request(API_Interface_Schemas.PROJECT_SET,data=data,callback=callback)
        
    def del_project(self,project_id=None,callback=None):
        data = {
            "project_id":project_id,
        }
        self._send_request(API_Interface_Schemas.PROJECT_DEL,url_kwargs=data,callback=callback)
        
    # endregion
    
    #  region 任务管理
    def query_tasks(self,name="",desc="",status=0,create_at=[],update_at=[],limit=10,page=1,order_by="create_at",callback=None):
        data = {
            "name":name,
            "desc":desc,
            "status":status,
            "create_at":create_at,
            "update_at":update_at,
            "limit":limit,
            "page":page,
            "order_by":order_by
        }
        self._send_request(API_Interface_Schemas.TASK_QUERY,query_data=data,callback=callback)
        
    def add_task(self,name="",desc="",status=0,datasets_id=0,project_id=0,callback=None):
        data = {
            "name":name,
            "desc":desc,
            "status":status,
            "datasets_id":datasets_id,
            "project_id":project_id,
        }
        self._send_request(API_Interface_Schemas.TASK_ADD,data=data,callback=callback)
        
    def set_task(self,task_id,name="",desc="",status=0,user_metadatas={},callback=None):
        data = {
            "name":name,
            "desc":desc,
            "status":status,
            "user_metadatas":user_metadatas,
        }
        self._send_request(API_Interface_Schemas.TASK_SET,data=data,url_kwargs={"task_id":task_id},callback=callback)
        
    def del_task(self,task_id=None,callback=None):
        data = {
            "task_id":task_id,
        }
        self._send_request(API_Interface_Schemas.TASK_DEL,url_kwargs=data,callback=callback)
        
    # endregion
        
    def check_login_status(self, callback=None):
        """检查登录状态，token过期则自动刷新"""
        if self._is_token_expired():
            if self.refresh_token:
                self._refresh_token(callback)
                return True
            else:
                self.request_error.emit("登录已过期，请重新登录")
                return False
        return True
    
    def get_user_info(self):
        """获取当前用户信息"""
        if self.check_login_status():
            self._send_request(API_Interface_Schemas.USER_GET)
    
    def get_file_mime_type(self, file_path):
        """根据文件后缀获取MIME类型（匹配接口要求）"""
        ext = os.path.splitext(file_path)[1].lower()
        if ext in ['.jpg', '.jpeg', '.png', '.bmp']:
            return f'image/{ext[1:]}'  # image/jpg, image/png等
        elif ext in ['.mp4', '.avi', '.mov']:
            return f'video/{ext[1:]}'  # video/mp4等
        elif ext == '.zip':
            return 'application/zip'
        return 'application/octet-stream'  # 默认二进制流

    def _send_request(self, api_interface, data=None, query_data=None, url_kwargs=None, callback=None):
        """发送网络请求（修正版）"""
        request_id = str(uuid.uuid4())
        api_name, api_url, op = api_interface
        url_kwargs = url_kwargs or {}
        
        # 格式化URL中的占位符(如{user_id})
        try:
            formatted_url = api_url.format(**url_kwargs)
        except KeyError as e:
            self.request_error.emit(f"URL格式错误: 缺少参数{e}")
            return
        
        # 构建完整URL
        url = QUrl(f"{self.base_url}{formatted_url}")
        if not url.isValid():
            self.request_error.emit(f"无效的URL: {url.toString()}")
            return
        if query_data:
            query = QUrlQuery()
            for key, value in query_data.items():
                if isinstance(value, list):
                    for item in value:
                        query.addQueryItem(str(key), str(item))
                else:
                    query.addQueryItem(str(key), str(value))
            url.setQuery(query)
        # 创建请求
        request = QNetworkRequest(url)
        uploadProgress = None
        errorOccurred = None
        if api_name in ["login"]:
            if api_name == "login":
                request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/x-www-form-urlencoded")
            request.setRawHeader(b"accept", b"application/json")
            data_str = ""
            for key, value in data.items():
                data_str += f"{key}={value}&"
            data_bytes = data_str.encode()
        elif api_name == "dataset_addfiles": # 
            data_id = data.get("id")
            file_path = data.get("file_path")
            uploadProgress = data.get("uploadProgress")
            errorOccurred = data.get("errorOccurred")
            # 3. 构建multipart/form-data请求体（文件上传必须用这种格式）
            # 注意：boundary是分隔符，必须唯一（这里用随机字符串）
            boundary = "----WebKitFormBoundary7ATo7zerXJJ44jKw"
            request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, f"multipart/form-data; boundary={boundary}")
            # 设置请求头（关键：包含认证信息）
            request.setRawHeader(b"accept", b"application/json")
                
            # 4. 构建请求数据
            multipart_data = QByteArray()


            # 添加文件字段
            file_name = os.path.basename(file_path)
            file_mime = self.get_file_mime_type(file_path)
            multipart_data.append(f'--{boundary}\r\n'.encode())
            # 注意：name必须和后端Form参数名一致（这里后端是"file"）
            multipart_data.append(
                f'Content-Disposition: form-data; name="files"; filename="{file_name}"\r\n'.encode()
            )
            multipart_data.append(f'Content-Type: {file_mime}\r\n'.encode())
            multipart_data.append(b'\r\n')  # 分隔文件内容和下一个字段
            # 读取文件内容到请求体
            try:
                with open(file_path, 'rb') as f:
                    file_data = f.read()
                multipart_data.append(file_data)
                multipart_data.append(b'\r\n')  # 分隔文件内容和下一个字段
            except Exception as e:
                # QMessageBox.critical(self, "文件读取失败", f"无法读取文件：{str(e)}")
                return
            
            # 添加data_id表单字段
            multipart_data.append(f'--{boundary}\r\n'.encode())
            multipart_data.append(b'Content-Disposition: form-data; name="data_id"\r\n')
            multipart_data.append(b'\r\n')
            multipart_data.append(f'{data_id}\r\n'.encode())
            
            multipart_data.append(f'--{boundary}--\r\n'.encode())  # 结束标记
            data_bytes = multipart_data
        else:
            data_bytes = json.dumps(data).encode() if data else None
            request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json")
        self._add_auth_header(request)
        
        # 存储请求信息
        self.requests[request_id] = {
            "type": api_name,
            "callback": callback,
            "timestamp": time.time()
        }
        
        # 发送请求（根据操作类型调用对应的公开方法）
        try:
            reply = None
            
            # 根据请求类型选择对应的公开方法
            # if api_name.endswith("_set"): # set 结尾 一般是 PATCH
            #     reply = self.nam.sendCustomRequest(request,b"PATCH",data_bytes if data_bytes else b"")
            if op == QNetworkAccessManager.Operation.PatchOperation: # set 结尾 一般是 PATCH
                reply = self.nam.sendCustomRequest(request,b"PATCH",data_bytes if data_bytes else b"")
            elif op == QNetworkAccessManager.Operation.GetOperation:
                reply = self.nam.get(request)
            elif op == QNetworkAccessManager.Operation.PostOperation:
                reply = self.nam.post(request, data_bytes) if data_bytes else self.nam.post(request, b"")
                # 执行回调
                if callable(uploadProgress):
                    # 连接上传相关信号（进度、错误）
                    reply.uploadProgress.connect(uploadProgress)
                if callable(errorOccurred):
                    # 连接上传相关信号（进度、错误）
                    reply.errorOccurred.connect(errorOccurred)
            elif op == QNetworkAccessManager.Operation.PutOperation:
                reply = self.nam.put(request, data_bytes) if data_bytes else self.nam.put(request, b"")
            elif op == QNetworkAccessManager.Operation.DeleteOperation:
                # 特殊处理：带JSON数据的DELETE请求
                if data_bytes:
                    # 手动构造DELETE请求并附加数据
                    request.setRawHeader(b"X-HTTP-Method-Override", b"DELETE")  # 兼容部分服务器
                    reply = self.nam.sendCustomRequest(request, b"DELETE", data_bytes)
                else:
                    # 无数据时使用默认DELETE方法
                    reply = self.nam.deleteResource(request)
            else:
                raise ValueError(f"不支持的请求类型: {op}")
            
            # 设置请求ID，用于响应处理
            reply.setProperty("request_id", request_id)
            self.requests[request_id]["reply"] = reply
        except Exception as e:
            self.request_error.emit(f"请求发送失败: {str(e)}")
            if request_id in self.requests:
                del self.requests[request_id]
                
    def _refresh_token(self, callback=None):
        """刷新access token"""
        if not self.refresh_token:
            self.request_error.emit("没有刷新令牌，无法刷新登录状态")
            return
        
        data = {
            "grant_type": "refresh_token",
            "refresh_token": self.refresh_token,
            "access_token": self.access_token
        }
        self._send_request(API_Interface_Schemas.REFRESH, data, callback=callback)
    
    def _add_auth_header(self, request):
        """为请求添加认证头"""
        if self.access_token:
            request.setRawHeader(b"Authorization", f"Bearer {self.access_token}".encode())
    
    def _is_token_expired(self):
        """检查token是否过期"""
        # 提前30秒刷新，避免网络延迟导致失效
        return time.time() + 30 > self.expires_at
    
    @Slot(QNetworkReply)
    def _handle_response(self, reply: QNetworkReply):
        """处理网络响应"""
        request_id = reply.property("request_id")
        if not request_id or request_id not in self.requests:
            self.request_error.emit("无法识别的请求响应")
            reply.deleteLater()
            return
            
        # 获取请求信息
        request_data = self.requests.pop(request_id)
        request_type = request_data["type"]
        callback = request_data["callback"]
        
        # 处理错误
        error = reply.error()
        if error != QNetworkReply.NoError:
            # QNetworkReply.NetworkError.AuthenticationRequiredError
            response_data = reply.readAll().data().decode('utf-8')
            error_msg = f"网络错误: {reply.errorString()}: {response_data}"
            self.request_result.emit(request_type, {"error": error_msg}, False)
            self.request_error.emit(error_msg)
            reply.deleteLater()
            logger.exception(error_msg)
            return
        
        # 解析响应数据
        try:
            response_data = reply.readAll().data().decode("utf-8")
            json_data = json.loads(response_data)
            
            # 处理业务状态码(假设200为成功)
            if json_data.get("code") == 1:
                returned_data = json_data.get("data", {})
                self.request_result.emit(request_type, returned_data, True)
                
                # 特殊接口的响应处理
                if request_type == "login":
                    self._handle_login_response(returned_data)
                elif request_type == "refresh":
                    self._handle_refresh_response(returned_data)
                # elif request_type == "user_get":
                #     self.request_result.emit(returned_data)
                
                # 执行回调
                if callable(callback):
                    callback(returned_data)
            else:
                # 业务错误
                error_msg = f"业务错误({json_data.get('code')}): {json_data.get('message', '未知错误')}"
                self.request_result.emit(request_type, {"error": error_msg}, False)
                self.request_error.emit(error_msg)
                logger.exception(error_msg)
                
        except json.JSONDecodeError:
            error_msg = f"响应格式错误: 无法解析JSON"
            self.request_result.emit(request_type, {"error": error_msg}, False)
            self.request_error.emit(error_msg)
            logger.exception(error_msg)
            
        except Exception as e:
            error_msg = f"响应处理失败: {str(e)}"
            self.request_result.emit(request_type, {"error": error_msg}, False)
            self.request_error.emit(error_msg)
            logger.exception(error_msg)
        reply.deleteLater()
    
    def _handle_login_response(self, data):
        """处理登录响应"""
        if "access_token" in data:
            self.set_userinfo(
                access_token=data["access_token"],
                refresh_token=data.get("refresh_token"),
                expires_in=data.get("expires_in", 3600)
            )
    
    def _handle_refresh_response(self, data):
        """处理token刷新响应"""
        if "access_token" in data:
            self.set_userinfo(
                access_token=data["access_token"],
                refresh_token=data.get("refresh_token"),
                expires_in=data.get("expires_in", 3600)
            )
            self.token_refreshed.emit()
        else:
            self.request_error.emit("Token刷新失败，请重新登录")
            self.access_token = None
            self.refresh_token = None

         
if __name__ == "__main__":
    app = QApplication(sys.argv)
    api = API("http://127.0.0.1:8000")
    api.login("admin", "123456")
    
    app.exec()
    # sys.exit(app.exec())